#!/usr/bin/env node

/**
 * 在本地开发npm模块的时候，我们可以使用npm link命令，将npm 模块链接到对应的运行项目中去，方便地对模块进行调试和测试
 * "@vue/compiler-sfc": "^3.0.0-rc.10"  compileTemplate的正确写法是这个版本
 */
const path = require('path')
const koa = require("koa")
const send = require("koa-send")
const compilerSFC = require('@vue/compiler-sfc') //处理单文件组件
const { Readable } = require('stream')
const app = new koa()

//把流转换成字符
const streamToString = stream => new Promise((resolve, reject) => {
    const chunks = []
    stream.on('data', chunk => chunks.push(chunk))
    stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')))
    stream.on('error', () => reject())
})

//把字符串转换成流
const stringToStream = string => {
    const stream = new Readable()
    stream.push(string)
    stream.push(null) //标识流被写完
    return stream
}


//3.加载第三方模块,  当请求的路径是以/@modules开头,改成对应的文件路径
app.use(async (ctx, next) => {
    //ctx.path --> /@modules/vue
    if(ctx.path.startsWith('/@modules/')) {
        const moduleName = ctx.path.substr(10)
        const pkgPath = path.join(process.cwd(), 'node_modules', moduleName, 'package.json')
        const pkg = require(pkgPath)
        ctx.path = path.join('/node_modules', moduleName, pkg.module)
    }
    await next()  //执行下一个中间件
})





//1.静态文件服务器，这个index.html是运行当前命令行工具的index.html
//process.cwd() 方法会返回 Node.js 进程的当前工作目录。
app.use(async (ctx, next) => {
    await send(ctx, ctx.path, { root: process.cwd(), index: 'index.html' })
    await next()
})

 
//4.处理单文件组件,读取文件,加载第三方模块前处理

app.use(async (ctx, next) => {
    if(ctx.path.endsWith('.vue')) {
        const contents = await streamToString(ctx.body)
        let { descriptor } = compilerSFC.parse(contents)
        let code
        if(!ctx.query.type) {  //第一次请求app.vue,将单文件组件编译成组件的选项对象返回给浏览器
            code = descriptor.script.content
            code = code.replace(/export\s+default\s+/g, 'const __script=')
            code += `
                import {render as __render} from "${ctx.path}?type=template"
                __script.render = __render
                export default __script
            `
            ctx.type = 'application/javascript'
            ctx.body = stringToStream(code)
        }else if(ctx.query.type === 'template') {
            //第二次请求app.vue,将单文件组件的模板编译成render函数
            const templateRender = compilerSFC.compileTemplate({source: descriptor.template.content })
            code = templateRender.code
            console.log(code);
        }
    }
    await next()
})


// 2.修改第三方模块的路径
app.use(async (ctx, next) => {
    if(ctx.type === 'application/javascript') {
        const contents = await streamToString(ctx.body)
        //s+： 多个空格  ?!: 不匹配这个分组的结果
        ctx.body = contents.
          replace(/(from\s+['"])(?![\.\/])/g, '$1/@modules/')
          .replace(/process\.env\.NODE_ENV/g, '"development"')
    }
})









app.listen(3000)
console.log('3000端口监听完毕');